package org.box.demos.multithreading.wgetmt.core;

/**
 * Lightweight alternative of {@link java.net.URL} plus new field - ContentType.
 * 
 * @author <a href="mailto:alexander.box@gmail.com">Alexander Korobka</a>
 * 
 */
public final class URL {

	private final String url;
	private final String origin;
	
	private ContentType contentType = ContentType.UNKNOWN;

	/**
	 * Constructs a new url instance.
	 * 
	 * @param url
	 *            - a string representation of the url
	 */
	public URL(String url) {
		if (url == null) {
			throw new IllegalArgumentException("url must not be null");
		}
		
		origin = url;
		int i, limit;
        // Remove embedded /./
        while ((i = url.indexOf("/./")) >= 0) {
        	url = url.substring(0, i) + url.substring(i + 2);
        }
        // Remove embedded /../ if possible
	    i = 0;
	    while ((i = url.indexOf("/../", i)) >= 0) {
			/* 
			 * A "/../" will cancel the previous segment and itself, 
			 * unless that segment is a "/../" itself
			 * i.e. "/a/b/../c" becomes "/a/c"
			 * but "/../../a" should stay unchanged
			 */
	    	
		    if (i > 0 && (limit = url.lastIndexOf('/', i - 1)) >= 0 
		    		&& (url.indexOf("/../", limit) != 0)) {
		    	
			    url = url.substring(0, limit) + url.substring(i + 3);
			    i = 0;
		    } else {
			    i = i + 3;
	        }
	    }
        // Remove trailing .. if possible
        while (url.endsWith("/..")) {
            i = url.indexOf("/..");
	        if ((limit = url.lastIndexOf('/', i - 1)) >= 0) {
	        	url = url.substring(0, limit+1);
	        } else {
	        	break;
	        }
        }
        
        // Remove starting .
        if (url.startsWith("./") && url.length() > 2)
            url = url.substring(2);

        // Remove trailing .
        if (url.endsWith("/."))
            url = url.substring(0, url.length() -1);

/*        if (!url.endsWith("/") && !url.contains("?") && (i = url.lastIndexOf("/")) > -1 
        		&& !url.substring(i).contains(".")) {
        	url = url + "/";
        }
*/        	
		this.url = url;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		return url.hashCode();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		URL other = (URL) obj;
		return url.equals(other.url);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return url;
	}

	/**
	 * Returns a query from the url. The query is a substring that starts after
	 * '?'. E.g.: <blockquote>
	 * 'http://gl.wikipedia.org/wiki/Xava?param1=true&amp;param=false'</blockquote
	 * > where the query is:
	 * <blockquote>param1=true&amp;param=false</blockquote>
	 * 
	 * @return the query as a string;
	 */
	public String getQuery() {
		int start = url.indexOf("?");
		return (start > -1) ? url.substring(start + 1) : "";
	}

	/**
	 * Returns a host name.
	 * 
	 * @return the host name as a string
	 */
	public String getHost() {
		int start = url.indexOf("//");
		if (start > -1) {
			int end = url.indexOf("/", start + 2);
			return (end > -1) ? url.substring(start + 2, end) : url
					.substring(start + 2);

		}
		return "";
	}

	/**
	 * Returns a path of the url including a host name, that is different to the
	 * java.net.URL implementation. E.g.:
	 * <blockquote>'gl.wikipedia.org/wiki/Xava'</blockquote>
	 * 
	 * @return the path as a string;
	 */
	public String getPath() {
		int start = url.indexOf("//");
		if (start > -1) {
			int end = url.indexOf("?", start + 2);
			return (end > -1) ? url.substring(start + 2, end) : url
					.substring(start + 2);
		} else {
			int end = url.indexOf("?");
			return (end > -1) ? url.substring(0, end) : url;
		}
	}

	/**
	 * Returns a protocol of the url. e.g.: <blockquote>'http://'</blockquote>
	 * 
	 * @return the protocol as a string.
	 */
	public String getProtocol() {
		int end = url.indexOf("//");
		return (end > -1) ? url.substring(0, end + 2) : "";
	}

	/**
	 * Returns a path without a file name, if <code>excludeFile</code> is true.
	 * e.g.: <code>excludeFile</code> is false:
	 * <blockquote>'gl.wikipedia.org/wiki/Xava'</blockquote>
	 * <code>excludeFile</code> is true:
	 * <blockquote>'gl.wikipedia.org/wiki/'</blockquote>
	 * 
	 * @param excludeFile
	 *            excludes or not the file name from the path
	 * 
	 * @return the path as a string
	 */
	public String getPath(boolean excludeFile) {
		String path = getPath();
		if (excludeFile) {
			if (path.endsWith("/")) {
				return path;
			} else {
				int index = path.lastIndexOf("/");
				if (index > -1) {
					return path.substring(0, index + 1);
				} else {
					return path + "/";
				}
			}
		} else {
			return path;
		}
	}

	/**
	 * Returns a number of the level in the given path. E.g.:
	 * <blockquote>'gl.wikipedia.org/wiki/'</blockquote> has 2 levels
	 * 
	 * @return a level's number
	 */
	public int getDeepLevel() {
		int level = 0;
		int index = 0;
		String path = getPath(true);
		while ((index = path.indexOf("/", index + 1)) > -1) {
			level++;
		}
		return level;
	}

	/**
	 * Returns a content type of the URL
	 * 
	 * @return one of the ContentType's constants
	 */
	public ContentType getContentType() {
		return contentType;
	}

	public void setContentType(ContentType contentType) {
		this.contentType = contentType;
	}

	public static String removeInnerPath(String urlStr) {
		if (urlStr.contains("#")) {
			int end = urlStr.indexOf('?');
			urlStr = urlStr.substring(0, urlStr.indexOf('#')) 
					+ ((end > 0 && end <urlStr.length()) ? urlStr.substring(end + 1) : "");
		}
		return urlStr;
	}

	public String getOrigin() {
		return origin;
	}

}
